﻿namespace RPCenter.WindowsAPI
{
    using Microsoft.VisualBasic.CompilerServices;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;

    public class NativeWindow
    {
        private string _ClassName;
        private IntPtr _Handle;
        private System.Drawing.Icon _Icon;
        private Process _OwningProcess;
        private string _Title;

        public NativeWindow(IntPtr Hwnd, string TitleText, Process Owner, string NativeClassName)
        {
            this._Title = string.Empty;
            this._ClassName = string.Empty;
            this._Handle = Hwnd;
            this._Title = TitleText;
            this._OwningProcess = Owner;
            this._ClassName = NativeClassName;
        }

        public NativeWindow(IntPtr Hwnd, string TitleText, Process Owner, string NativeClassName, System.Drawing.Icon WindowIcon) : this(Hwnd, TitleText, Owner, NativeClassName)
        {
            this._Icon = WindowIcon;
        }

        private void CheckHandle(string MethodName)
        {
            if (this.Handle == IntPtr.Zero)
            {
                throw new ApplicationException("Handle property must be set to a valid window handle before calling " + MethodName);
            }
        }

        public void Close()
        {
            this.CheckHandle("Close");
            ApiDefinitions.SendMessage(this.Handle, 0x10, 0, 0);
        }

        private static bool EnumWindowsCallBack(IntPtr handle, IntPtr lParam)
        {
            EnumWindowsArgs target = (EnumWindowsArgs) GCHandle.FromIntPtr(lParam).Target;
            if (target.IncludeInvisible)
            {
                target.WindowList.Add(FromHandle(handle));
            }
            else if (ApiDefinitions.IsWindowVisible(handle))
            {
                target.WindowList.Add(FromHandle(handle));
            }
            return true;
        }

        public void Flash()
        {
            this.CheckHandle("Flash");
            FlashWindow(this.Handle, FlashItemOption.FlashTitleAndTaskbar, FlashDurationOption.FlashUntilWindowActivated);
        }

        public static void FlashWindow(IntPtr Handle, FlashItemOption Item, FlashDurationOption Duration)
        {
            ApiDefinitions.FLASHWINFO flashwinfo;
            flashwinfo = new ApiDefinitions.FLASHWINFO {
                hwnd = Handle,
                dwFlags = (uint) (Item | ((FlashItemOption) ((uint) Duration))),
                cbSize = (uint) Marshal.SizeOf(uint.MaxValue)
            };
            ApiDefinitions.FlashWindowEx(ref flashwinfo);
        }

        public static NativeWindow FromHandle(IntPtr Hwnd)
        {
            StringBuilder lpString = new StringBuilder(ApiDefinitions.GetWindowTextLength(Hwnd) + 1);
            ApiDefinitions.GetWindowText(Hwnd, lpString, lpString.Capacity);
            int lpdwProcessId = -1;
            ApiDefinitions.GetWindowThreadProcessId(Hwnd, out lpdwProcessId);
            StringBuilder lpClassName = new StringBuilder(0xff);
            ApiDefinitions.GetClassName(Hwnd, lpClassName, lpClassName.Capacity);
            IntPtr handle = ApiDefinitions.SendMessage(Hwnd, 0x7f, 0, 0);
            if (handle == IntPtr.Zero)
            {
                handle = GetClassLongCpuIndependent(Hwnd, -34);
            }
            System.Drawing.Icon windowIcon = null;
            if (handle != IntPtr.Zero)
            {
                try
                {
                    windowIcon = System.Drawing.Icon.FromHandle(handle);
                }
                catch (Exception exception1)
                {
                    ProjectData.SetProjectError(exception1);
                    windowIcon = null;
                    ProjectData.ClearProjectError();
                }
            }
            return new NativeWindow(Hwnd, lpString.ToString(), Process.GetProcessById(lpdwProcessId), lpClassName.ToString(), windowIcon);
        }

        public static NativeWindow GetActiveWindow()
        {
            IntPtr foregroundWindow = ApiDefinitions.GetForegroundWindow();
            if (foregroundWindow == IntPtr.Zero)
            {
                throw new ApplicationException("Could not find active window. The last error reported was: " + new Win32Exception().Message);
            }
            return FromHandle(foregroundWindow);
        }

        public List<NativeWindow> GetChildren()
        {
            this.CheckHandle("GetChildren");
            return GetChildWindows(this.Handle);
        }

        public static List<NativeWindow> GetChildWindows(IntPtr ParentWindowHandle)
        {
            List<NativeWindow> list = new List<NativeWindow>();
            GCHandle handle = GCHandle.Alloc(list);
            try
            {
                ApiDefinitions.EnumChildWindows(ParentWindowHandle, new ApiDefinitions.EnumWindowsProc(NativeWindow.EnumWindowsCallBack), GCHandle.ToIntPtr(handle));
            }
            finally
            {
                handle.Free();
            }
            return list;
        }

        private static IntPtr GetClassLongCpuIndependent(IntPtr hwnd, int nIndex)
        {
            if (IntPtr.Size == 4)
            {
                return ApiDefinitions.GetClassLong(hwnd, nIndex);
            }
            return ApiDefinitions.GetClassLongPtr(hwnd, nIndex);
        }

        public Size GetSize()
        {
            this.CheckHandle("GetSize");
            return GetWindowSize(this.Handle);
        }

        public static List<NativeWindow> GetTopLevelWindows()
        {
            EnumWindowsArgs args2 = new EnumWindowsArgs {
                WindowList = new List<NativeWindow>(),
                IncludeInvisible = false
            };
            EnumWindowsArgs args = args2;
            GCHandle handle = GCHandle.Alloc(args);
            try
            {
                ApiDefinitions.EnumWindows(new ApiDefinitions.EnumWindowsProc(NativeWindow.EnumWindowsCallBack), GCHandle.ToIntPtr(handle));
            }
            finally
            {
                handle.Free();
            }
            return args.WindowList;
        }

        public static List<NativeWindow> GetTopLevelWindows(bool IncludeInvisibleWindows)
        {
            EnumWindowsArgs args2 = new EnumWindowsArgs {
                WindowList = new List<NativeWindow>(),
                IncludeInvisible = IncludeInvisibleWindows
            };
            EnumWindowsArgs args = args2;
            GCHandle handle = GCHandle.Alloc(args);
            try
            {
                ApiDefinitions.EnumWindows(new ApiDefinitions.EnumWindowsProc(NativeWindow.EnumWindowsCallBack), GCHandle.ToIntPtr(handle));
            }
            finally
            {
                handle.Free();
            }
            return args.WindowList;
        }

        public static NativeWindow GetWindowFromTitle(string Title)
        {
            IntPtr hwnd = ApiDefinitions.FindWindowW(null, Title);
            if (hwnd == IntPtr.Zero)
            {
                throw new ApplicationException("No window with the title \"" + Title + "\" could be found");
            }
            return FromHandle(hwnd);
        }

        public static List<NativeWindow> GetWindowsFromPartialTitle(string PartialTitle)
        {
            List<NativeWindow> topLevelWindows = GetTopLevelWindows();
            for (int i = topLevelWindows.Count - 1; i >= 0; i += -1)
            {
                if (topLevelWindows[i].Title.IndexOf(PartialTitle, StringComparison.CurrentCultureIgnoreCase) <= -1)
                {
                    topLevelWindows.RemoveAt(i);
                }
            }
            return topLevelWindows;
        }

        public static Size GetWindowSize(IntPtr Handle)
        {
            ApiDefinitions.RECT lpRect = new ApiDefinitions.RECT();
            if (!ApiDefinitions.GetWindowRect(Handle, out lpRect))
            {
                throw new ApplicationException("Unable to get size of window. The last error reported was: " + new Win32Exception().Message);
            }
            return new Size(lpRect.right - lpRect.left, lpRect.bottom - lpRect.top);
        }

        public static Size GetWindowSize(string Title)
        {
            ApiDefinitions.RECT lpRect = new ApiDefinitions.RECT();
            if (!ApiDefinitions.GetWindowRect(GetWindowFromTitle(Title).Handle, out lpRect))
            {
                throw new ApplicationException("Unable to get size of window. The last error reported was: " + new Win32Exception().Message);
            }
            return new Size(lpRect.right - lpRect.left, lpRect.bottom - lpRect.top);
        }

        public void Hide()
        {
            this.CheckHandle("Hide");
            SetWindowState(this.Handle, WindowStateOption.Hide);
        }

        public void MakeNotTopMost()
        {
            this.CheckHandle("MakeNotTopmost");
            MakeWindowNotTopMost(this.Handle);
        }

        public void MakeTopMost()
        {
            this.CheckHandle("MakeTopmost");
            MakeWindowTopMost(this.Handle);
        }

        public static void MakeWindowNotTopMost(NativeWindow TargetWindow)
        {
            if (TargetWindow == null)
            {
                throw new ArgumentNullException("TargetWindow");
            }
            MakeWindowNotTopMost(TargetWindow.Handle);
        }

        public static void MakeWindowNotTopMost(IntPtr TargetWindowHandle)
        {
            if (TargetWindowHandle == IntPtr.Zero)
            {
                throw new ArgumentException("The window handle is not valid");
            }
            IntPtr hWndInsertAfter = new IntPtr(-2);
            if (!ApiDefinitions.SetWindowPos(TargetWindowHandle, hWndInsertAfter, 0, 0, 0, 0, 3))
            {
                throw new ApplicationException("Unable to make window not topmost. The last error reported was: " + new Win32Exception().Message);
            }
        }

        public static void MakeWindowTopMost(NativeWindow TargetWindow)
        {
            if (TargetWindow == null)
            {
                throw new ArgumentNullException("TargetWindow");
            }
            MakeWindowTopMost(TargetWindow.Handle);
        }

        public static void MakeWindowTopMost(IntPtr TargetWindowHandle)
        {
            if (TargetWindowHandle == IntPtr.Zero)
            {
                throw new ArgumentException("The window handleis not valid");
            }
            IntPtr hWndInsertAfter = new IntPtr(-1);
            if (!ApiDefinitions.SetWindowPos(TargetWindowHandle, hWndInsertAfter, 0, 0, 0, 0, 3))
            {
                throw new ApplicationException("Unable to make window topmost. The last error reported was: " + new Win32Exception().Message);
            }
        }

        public void Maximize()
        {
            this.CheckHandle("Maximize");
            SetWindowState(this.Handle, WindowStateOption.Maximize);
        }

        public void Minimize()
        {
            this.CheckHandle("Minimize");
            SetWindowState(this.Handle, WindowStateOption.Minimize);
        }

        public void Restore()
        {
            this.CheckHandle("Restore");
            this.Show();
        }

        public void SetParent(NativeWindow ParentWindow)
        {
            this.CheckHandle("SetParent");
            SetParentWindow(this, ParentWindow);
        }

        public static void SetParentWindow(NativeWindow Child, NativeWindow Parent)
        {
            if (Child == null)
            {
                throw new ArgumentNullException("Child");
            }
            if (Parent == null)
            {
                throw new ArgumentNullException("Parent");
            }
            if (Child.Handle == IntPtr.Zero)
            {
                throw new ArgumentException("The Handle property of the Child window is not valid");
            }
            if (Parent.Handle == IntPtr.Zero)
            {
                throw new ArgumentException("The Handle property of the Parent window is not valid");
            }
            if (ApiDefinitions.SetParent(Child.Handle, Parent.Handle) == IntPtr.Zero)
            {
                throw new ApplicationException("Unable to set the parent of the window. The last error reported was: " + new Win32Exception().Message);
            }
        }

        public static void SetWindowState(IntPtr hwnd, WindowStateOption state)
        {
            ApiDefinitions.ShowWindow(hwnd, (int) state);
        }

        public void Show()
        {
            this.CheckHandle("Show");
            SetWindowState(this.Handle, WindowStateOption.Normal);
        }

        public static void StopFlashWindow(IntPtr Handle)
        {
            ApiDefinitions.FLASHWINFO flashwinfo;
            flashwinfo = new ApiDefinitions.FLASHWINFO {
                hwnd = Handle,
                dwFlags = 0,
                cbSize = (uint) Marshal.SizeOf(uint.MaxValue)
            };
            ApiDefinitions.FlashWindowEx(ref flashwinfo);
        }

        public override string ToString()
        {
            if (this.OwningProcess == null)
            {
                return (this.Handle.ToString() + " ---- " + this.ClassName + " ---- " + this.Title);
            }
            return (this.Handle.ToString() + " ---- " + this.ClassName + " ---- " + this.Title + " ---- " + this.OwningProcess.ProcessName);
        }

        public string ClassName
        {
            get
            {
                return this._ClassName;
            }
            set
            {
                this._ClassName = value;
            }
        }

        public IntPtr Handle
        {
            get
            {
                return this._Handle;
            }
            set
            {
                this._Handle = value;
            }
        }

        public System.Drawing.Icon Icon
        {
            get
            {
                return this._Icon;
            }
            set
            {
                this._Icon = value;
            }
        }

        public Process OwningProcess
        {
            get
            {
                return this._OwningProcess;
            }
            set
            {
                this._OwningProcess = value;
            }
        }

        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                this._Title = value;
            }
        }

        private class EnumWindowsArgs
        {
            private bool _IncludeInvisible;
            private List<NativeWindow> _WindowList;

            public bool IncludeInvisible
            {
                get
                {
                    return this._IncludeInvisible;
                }
                set
                {
                    this._IncludeInvisible = value;
                }
            }

            public List<NativeWindow> WindowList
            {
                get
                {
                    return this._WindowList;
                }
                set
                {
                    this._WindowList = value;
                }
            }
        }

        public enum FlashDurationOption : uint
        {
            FlashUntilFlashStopSet = 4,
            FlashUntilWindowActivated = 12
        }

        public enum FlashItemOption : uint
        {
            FlashTaskbar = 2,
            FlashTitleAndTaskbar = 3,
            FlashTitleBar = 1
        }

        public enum WindowStateOption
        {
            Hide = 0,
            Maximize = 3,
            Minimize = 6,
            Normal = 1
        }
    }
}

